package org.mybatis.generator.plugins;

import java.util.ArrayList;
import java.util.List;

import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;

/**
 * 自定义 Mapper&Service 插件
 *
 */
public class MybatisServicePlugin extends PluginAdapter {

	private FullyQualifiedJavaType springAutowired;
	private FullyQualifiedJavaType springService;
	private FullyQualifiedJavaType slf4jLogger;
	private FullyQualifiedJavaType slf4jLoggerFactory;
	private FullyQualifiedJavaType myBatisMapper;

	private FullyQualifiedJavaType baseService;
	private FullyQualifiedJavaType baseMapper;
	private FullyQualifiedJavaType redis;

	private FullyQualifiedJavaType service;
	private FullyQualifiedJavaType mapper;
	private FullyQualifiedJavaType entity;
	private FullyQualifiedJavaType entityExample;

	private String serviceTargetPackage;
	private String targetProject;
	private String entityPackage;

	@Override
	public boolean validate(List<String> warnings) {
		serviceTargetPackage = properties.getProperty("serviceTargetPackage");
		targetProject = properties.getProperty("targetProject");
		entityPackage = context.getJavaModelGeneratorConfiguration().getTargetPackage();

		myBatisMapper = new FullyQualifiedJavaType(properties.getProperty("myBatisMapper"));
		redis = new FullyQualifiedJavaType(properties.getProperty("redis"));
		

		slf4jLogger = new FullyQualifiedJavaType("org.slf4j.Logger");
		slf4jLoggerFactory = new FullyQualifiedJavaType("org.slf4j.LoggerFactory");
		springAutowired = new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired");
		springService = new FullyQualifiedJavaType("org.springframework.stereotype.Service");

		return true;
	}

	@Override
	public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
		List<GeneratedJavaFile> files = new ArrayList<GeneratedJavaFile>();
		String table = introspectedTable.getBaseRecordType();
		String tableName = table.replaceAll(this.entityPackage + ".", "");

		service = new FullyQualifiedJavaType(serviceTargetPackage + "." + tableName + "Service");
		mapper = new FullyQualifiedJavaType(introspectedTable.getMyBatis3JavaMapperType());
		entity = new FullyQualifiedJavaType(entityPackage + "." + tableName);
		entityExample = new FullyQualifiedJavaType(entityPackage + "." + tableName + "Example");
		baseService = new FullyQualifiedJavaType(properties.getProperty("baseService"));
		baseMapper = new FullyQualifiedJavaType(properties.getProperty("baseMapper"));
		
		baseMapper.addTypeArgument(entity);
		baseMapper.addTypeArgument(new FullyQualifiedJavaType("java.lang.Long"));
		baseMapper.addTypeArgument(entityExample);

		baseService.addTypeArgument(entity);
		baseService.addTypeArgument(new FullyQualifiedJavaType("java.lang.Long"));
		baseService.addTypeArgument(entityExample);

		// 重写 mapper 文件
		overrideMapperClass(new Interface(mapper), introspectedTable, tableName, files);
		// 生成 service 文件
		addServiceClass(new TopLevelClass(service), introspectedTable, tableName, files);

		return files;
	}

	// mapperClass
	protected void overrideMapperClass(Interface interface1, IntrospectedTable introspectedTable, String tableName, List<GeneratedJavaFile> files) {
		interface1.addImportedType(baseMapper);
		interface1.addImportedType(myBatisMapper);
		interface1.addImportedType(entity);
		interface1.addImportedType(entityExample);

		interface1.setVisibility(JavaVisibility.PUBLIC);
		interface1.addAnnotation("@MyBatisMapper(\"" + tableName.substring(0, 1).toLowerCase() + tableName.substring(1) + "Mapper\")");
		interface1.addSuperInterface(baseMapper);

		GeneratedJavaFile file = new GeneratedJavaFile(interface1, targetProject, context.getJavaFormatter());
		files.add(file);
	}

	// serviceClass
	protected void addServiceClass(TopLevelClass topLevelClass, IntrospectedTable introspectedTable, String tableName, List<GeneratedJavaFile> files) {
		topLevelClass.addImportedType(springService);
		topLevelClass.addImportedType(springAutowired);
		topLevelClass.addImportedType(slf4jLogger);
		topLevelClass.addImportedType(slf4jLoggerFactory);
		topLevelClass.addImportedType(baseMapper);
		topLevelClass.addImportedType(baseService);

		topLevelClass.addImportedType(mapper);
		topLevelClass.addImportedType(redis);
		topLevelClass.addImportedType(entity);
		topLevelClass.addImportedType(entityExample);

		topLevelClass.setVisibility(JavaVisibility.PUBLIC);
		topLevelClass.addAnnotation("@Service(\"" + tableName.substring(0, 1).toLowerCase() + tableName.substring(1) + "Service\")");
		topLevelClass.setSuperClass(baseService);

		// mapper
		Field field = new Field();
		field.setName(toLowerCase(mapper.getShortName()));
		topLevelClass.addImportedType(mapper);
		field.setType(mapper);
		field.setVisibility(JavaVisibility.PROTECTED);
		field.addAnnotation("@Autowired");
		topLevelClass.addField(field);

		// Redis
		field = new Field();
		field.setName(toLowerCase(redis.getShortName()));
		topLevelClass.addImportedType(redis);
		field.setType(redis);
		field.setVisibility(JavaVisibility.PROTECTED);
		field.addAnnotation("@Autowired");
		topLevelClass.addField(field);

		// logger
		field = new Field();
		field.setFinal(true);
		field.setInitializationString("LoggerFactory.getLogger(getClass())");
		field.setName("logger");
		field.setType(new FullyQualifiedJavaType("Logger"));
		field.setVisibility(JavaVisibility.PROTECTED);
		topLevelClass.addField(field);

		Method method = new Method();
		method.addAnnotation("@Override");
		method.setName("getBaseMapper");

		method.setReturnType(baseMapper);
		method.setVisibility(JavaVisibility.PROTECTED);

		StringBuilder sb = new StringBuilder();
		sb.append("if (super.getBaseMapper() == null) {");
		sb.append("super.setBaseMapper(" + toLowerCase(mapper.getShortName()) + ");");
		sb.append("}");
		sb.append("return super.getBaseMapper();");
		method.addBodyLine(sb.toString());

		topLevelClass.addMethod(method);
		
		GeneratedJavaFile file = new GeneratedJavaFile(topLevelClass, targetProject, context.getJavaFormatter());
		files.add(file);
	}

	protected String toLowerCase(String tableName) {
		StringBuilder sb = new StringBuilder(tableName);
		sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
		return sb.toString();
	}
}
